﻿using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace Najak
{
	public partial class InputHandler
	{
		static public InputHandler Instance = new InputHandler();

		static public KeyboardState GetKeyboardState()
		{
			if (App.IsKeyboardDisabled)
			{
				return new KeyboardState();
			}
			else
			{
				KeyboardState keyboardState = Keyboard.GetState();
				return keyboardState;
			}
		}
		static public MouseState GetMouseState()
		{
			return LastMouseState;
		}
		static private MouseState UpdateMouseState()
		{
			var ms = Mouse.GetState();

			if (App.Instance.AppHost.IsMousePositionAlreadyCorrect)
			{
				LastMouseState = new MouseState(ms.X, ms.Y, ms.ScrollWheelValue, ms.LeftButton, ms.MiddleButton, ms.RightButton, ms.XButton1, ms.XButton2);
			}
			else
			{
				var loc = App.Instance.ScreenLocation;
				int x = ms.X - loc.X;
				int y = ms.Y - loc.Y;
				LastMouseState = new MouseState(x, y, ms.ScrollWheelValue, ms.LeftButton, ms.MiddleButton, ms.RightButton, ms.XButton1, ms.XButton2);
			}

			return LastMouseState;
		}
		static private MouseState LastMouseState;

		public Point MouseScreenLocation;
		public Point MouseAbsoluteLocation;
		public Coordinate MouseMapLocation;

		public int WheelDelta;

		public MouseButtonState CurrentMouseButtonState = new MouseButtonState();
		public MouseButtonState PreviousMouseButtonState = new MouseButtonState();

		public KeyboardModes KeyboardMode = KeyboardModes.Normal;

		public bool IsShiftPressed { get { return (KeyboardMode & KeyboardModes.Shift) != 0; } }
		public bool IsCtrlPressed { get { return (KeyboardMode & KeyboardModes.Ctrl) != 0; } }
		public bool IsAltPressed { get { return (KeyboardMode & KeyboardModes.Alt) != 0; } }
		public bool IsMouseOverScreen { get; private set; }

		public void CheckForEvents()
		{
			_UpdateMode();
			_UpdateMouseState();
			_CheckForKeyEvents();
			_CheckForMouseEvents();
		}

		private void _UpdateMode()
		{
			KeyboardState keyboardState = GetKeyboardState();

			KeyboardModes mode = KeyboardModes.Normal;

			if (keyboardState.IsKeyDown(Keys.LeftShift) || keyboardState.IsKeyDown(Keys.RightShift))
				mode |= KeyboardModes.Shift;

			if (keyboardState.IsKeyDown(Keys.LeftControl) || keyboardState.IsKeyDown(Keys.RightControl))
				mode |= KeyboardModes.Ctrl;

			if (keyboardState.IsKeyDown(Keys.LeftAlt) || keyboardState.IsKeyDown(Keys.RightAlt))
				mode |= KeyboardModes.Alt;

			KeyboardMode = mode;
		}

		private void _UpdateMouseState()
		{
			MouseState ms = UpdateMouseState();

			MouseScreenLocation = new Point(ms.X, ms.Y);

			Point vpos = new Point((int)Scene.Instance.VportOffset.X, (int)Scene.Instance.VportOffset.Y);
			MouseAbsoluteLocation = new Point(MouseScreenLocation.X + vpos.X, MouseScreenLocation.Y + vpos.Y);


			int row = (int)(MouseAbsoluteLocation.Y / Tile.Size.Y);
			int col = (int)(MouseAbsoluteLocation.X / Tile.Size.X);
			MouseMapLocation = new Coordinate(col, row);


			MouseButtons btns = MouseButtons.None;

			if (ms.LeftButton == ButtonState.Pressed)
				btns |= MouseButtons.Left;

			if (ms.MiddleButton == ButtonState.Pressed)
				btns |= MouseButtons.Middle;

			if (ms.RightButton == ButtonState.Pressed)
				btns |= MouseButtons.Right;

			PreviousMouseButtonState = CurrentMouseButtonState;
			CurrentMouseButtonState = new MouseButtonState(btns);
		}

		private void _CheckForMouseEvents()
		{
			Point mp = MouseScreenLocation;
			bool isMouseOverScreen = (mp.X >= 0 && mp.Y >= 0 && mp.X < App.Instance.ScreenSize.X && mp.Y < App.Instance.ScreenSize.Y);

			if (!IsMouseOverScreen)
			{
				if (isMouseOverScreen)
				{
					if (MouseIsNowOverscreen != null)
						MouseIsNowOverscreen();
				}

				IsMouseOverScreen = isMouseOverScreen;
				return;
			}
			if (!isMouseOverScreen)
			{
				CurrentMouseButtonState.Buttons = MouseButtons.None;
			}

			MouseButtonState mb = CurrentMouseButtonState;
			MouseButtonState pmb = PreviousMouseButtonState;

			for (int i = (RegisteredMouseEvents.Count - 1); i >= 0; i--)
			{
				MouseEvent evt = RegisteredMouseEvents[i];

				bool isTriggered = false;

				evt.WasLeftClick = false;
				evt.WasMiddleClick = false;
				evt.WasRightClick = false;

				if (evt.IsWheelChanged)
				{
					evt.WheelDelta = WheelDelta;
					if (WheelDelta != 0)
					{
						evt.TypesActivated |= MouseEventTypes.WheelChanged;
						isTriggered = true;
					}
				}

				if (evt.IsButtonClick || evt.IsButtonUp)
				{
					if (evt.IsLeftButton)
						evt.WasLeftClick |= _DetectClick(mb.IsLeftMouseDown, pmb.IsLeftMouseDown, MouseButtons.Left, ref evt.StartEventLocation, ref evt.TypesActivated, ref evt.ButtonsActivated);
					if (evt.IsMiddleButton)
						evt.WasMiddleClick |= _DetectClick(mb.IsMiddleMouseDown, pmb.IsMiddleMouseDown, MouseButtons.Middle, ref evt.StartEventLocation, ref evt.TypesActivated, ref evt.ButtonsActivated);
					if (evt.IsRightButton)
						evt.WasRightClick |= _DetectClick(mb.IsRightMouseDown, pmb.IsRightMouseDown, MouseButtons.Right, ref evt.StartEventLocation, ref evt.TypesActivated, ref evt.ButtonsActivated);
				}

				if (evt.IsButtonClick)
				{	// Handle Button Click
					if (evt.IsLeftButton)
						isTriggered |= evt.WasLeftClick;
					if (evt.IsMiddleButton)
						isTriggered |= evt.WasMiddleClick;
					if (evt.IsRightButton)
						isTriggered |= evt.WasRightClick;
				}

				if (evt.IsButtonDown)
				{
					if (evt.IsLeftButton)
						isTriggered |= !pmb.IsLeftMouseDown && mb.IsLeftMouseDown;
					if (evt.IsRightButton)
						isTriggered |= !pmb.IsRightMouseDown && mb.IsRightMouseDown;
					if (evt.IsMiddleButton)
						isTriggered |= !pmb.IsMiddleMouseDown && mb.IsMiddleMouseDown;
				}
				if (evt.IsButtonUp)
				{
					if (evt.IsLeftButton)
						isTriggered |= pmb.IsLeftMouseDown && !mb.IsLeftMouseDown;
					if (evt.IsRightButton)
						isTriggered |= pmb.IsRightMouseDown && !mb.IsRightMouseDown;
					if (evt.IsMiddleButton)
						isTriggered |= pmb.IsMiddleMouseDown && !mb.IsMiddleMouseDown;
				}
				//if (evt.IsMove || evt.IsDrag)
				//{
				//}

				// TODO:  Drag, Move

				if (isTriggered)
				{
					evt.ScreenLocation = MouseScreenLocation;
					evt.AbsoluteLocation = MouseAbsoluteLocation;
					evt.MapCoordinate = MouseMapLocation;
					evt.Action(evt);
				}
			}

			IsMouseOverScreen = isMouseOverScreen;
			WheelDelta = 0;
		}
		static public event Action MouseIsNowOverscreen;

		private bool _DetectClick(bool isDown, bool wasDown, MouseButtons btn, ref Point startPos, ref MouseEventTypes activatedTypes, ref MouseButtons buttonsActivated)
		{
			if (wasDown && !isDown)
			{
				int dist2 = MathUtil.FindDistanceSquared(MouseScreenLocation, startPos);
				if (dist2 <= 144)
				{
					activatedTypes |= MouseEventTypes.ButtonClick;
					buttonsActivated |= btn;
					return true;
				}
			}
			else if (!wasDown && isDown)
			{	// button was just pressed
				startPos = MouseScreenLocation;
			}
			return false;
		}

		private void _CheckForKeyEvents()
		{
			KeyboardState keyboardState = GetKeyboardState();

			if (App.IsPaused && keyboardState.IsKeyDown(Keys.Enter))
				return;

			int timeNow = System.Environment.TickCount;

			for (int i = (RegisteredKeys.Count - 1); i >= 0; i--)
			{
				KeyPressEvent evt = RegisteredKeys[i];
				if (evt.EnableRepeat)
				{
					if (timeNow > evt.TimeForNextRepeat)
					{
						evt.IsKeyDown = false;
					}
				}
				if (evt.IsKeyDown == false)
				{
					if (keyboardState.IsKeyDown(evt.Key))
					{
						if (evt.TimeForNextRepeat == 0)
						{
							evt.TimeForNextRepeat = timeNow + 500;
						}
						else
						{
							evt.TimeForNextRepeat = timeNow + 90;
						}
						evt.IsKeyDown = true;


						if (evt.RequireCtrl)
						{
							if (IsCtrlPressed)
							{
								evt.Action(evt.Key);
							}
						}
						else
						{
							if (!IsCtrlPressed)
								evt.Action(evt.Key);
						}
						if (evt.OnlyOnce)
							RegisteredKeys.RemoveAt(i);


					}
				}
				else if (keyboardState.IsKeyUp(evt.Key))
				{
					evt.IsKeyDown = false;
					evt.TimeForNextRepeat = 0;
				}
			}
		}
	}
}
